#include "XmlSerializer.h"

XmlSerializer::XmlSerializer(char* filename)
{
	this->filename = NULL;
	this->xmlWriter = NULL;
	this->document = NULL;

	int size = strlen(filename);
	this->filename = (char*)malloc((size + 1) * sizeof(char));
	strcpy_s(this->filename, size + 1, filename);
}

XmlSerializer::~XmlSerializer(void)
{
	if (filename != NULL) free(filename);
	if (xmlWriter != NULL) xmlFreeTextWriter(xmlWriter);
	if (document != NULL) xmlFreeDoc(document);
}

void XmlSerializer::BeginSerialization()
{
	xmlWriter = xmlNewTextWriterFilename(filename, 0);
	xmlTextWriterStartDocument(xmlWriter, NULL, NULL, NULL);
}

void XmlSerializer::EndSerialization()
{
	xmlTextWriterEndDocument(xmlWriter);
	xmlFreeTextWriter(xmlWriter);
	xmlWriter = NULL;
}

void XmlSerializer::BeginDeserialization()
{
	document = xmlReadFile(filename, NULL, 0);
	currentNode = xmlDocGetRootElement(document);
}

void XmlSerializer::EndDeserialization()
{
	xmlFreeDoc(document);
	document = NULL;
	currentNode = NULL;
}

void XmlSerializer::StartItem(char* name, char* type, char* version)
{
	xmlTextWriterStartElement(xmlWriter, BAD_CAST(name));
	xmlTextWriterWriteAttribute(xmlWriter, BAD_CAST("type"), BAD_CAST(type));
	xmlTextWriterWriteAttribute(xmlWriter, BAD_CAST("version"), BAD_CAST(version));
}

void XmlSerializer::StartItem(char* name)
{
	xmlTextWriterStartElement(xmlWriter, BAD_CAST(name));
}

void XmlSerializer::EndItem(char* name)
{
	(void)(name);
	xmlTextWriterEndElement(xmlWriter);
}

void XmlSerializer::SerializeProperty(char* name, bool value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%b", value);
}

void XmlSerializer::SerializeProperty(char* name, signed char value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%c", value);
}

void XmlSerializer::SerializeProperty(char* name, unsigned char value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%c", value);
}

void XmlSerializer::SerializeProperty(char* name, signed short int value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%d", value);
}

void XmlSerializer::SerializeProperty(char* name, unsigned short int value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%u", value);
}

void XmlSerializer::SerializeProperty(char* name, signed long int value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%d", value);
}

void XmlSerializer::SerializeProperty(char* name, unsigned long int value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%u", value);
}

void XmlSerializer::SerializeProperty(char* name, float value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%f", value);
}

void XmlSerializer::SerializeProperty(char* name, double value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%f", value);
}

void XmlSerializer::SerializeProperty(char* name, long double value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%f", value);
}

void XmlSerializer::SerializeProperty(char* name, signed long long int value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%d", value);
}

void XmlSerializer::SerializeProperty(char* name, unsigned long long int value)
{
	xmlTextWriterWriteFormatElement(xmlWriter, BAD_CAST(name), "%u", value);
}

void XmlSerializer::SerializeProperty(char* name, char* value)
{
	xmlTextWriterWriteElement(xmlWriter, BAD_CAST(name), BAD_CAST(value));
}

char* XmlSerializer::CurrentName()
{
	return (char*)currentNode->name;
}

char* XmlSerializer::CurrentValue()
{
	xmlNodePtr childNode = currentNode->children;
	do
	{
		if (childNode->type == XML_TEXT_NODE)
		{
			return (char*)childNode->content;
		}
	} while (childNode != currentNode->last);

	return NULL;
}

char* XmlSerializer::CurrentType()
{
	for (xmlAttrPtr propertyNode = currentNode->properties; propertyNode != NULL; propertyNode = propertyNode->next)
	{
		if (propertyNode->type == XML_ATTRIBUTE_NODE && strcmp((const char*)propertyNode->name, "type") == 0)
		{
			return (char*)propertyNode->children->content;
		}
	}

	return NULL;
}

char* XmlSerializer::CurrentVersion()
{
	for (xmlAttrPtr propertyNode = currentNode->properties; propertyNode != NULL; propertyNode = propertyNode->next)
	{
		if (propertyNode->type == XML_ATTRIBUTE_NODE && strcmp((const char*)propertyNode->name, "version") == 0)
		{
			return (char*)propertyNode->children->content;
		}
	}

	return NULL;
}

char* XmlSerializer::ChildName(int index)
{
	xmlNodePtr child = findChild(index);
	if (child == NULL)
	{
		return NULL;
	}
	else
	{
		return (char*)child->name;
	}
}

char* XmlSerializer::ChildValue(int index)
{
	xmlNodePtr child = findChild(index);
	
	for (xmlNodePtr subNode = child->children; subNode != child->last->next; subNode = subNode->next)
	{
		if (subNode->type == XML_TEXT_NODE)
		{
			return (char*)subNode->content;
		}
	}

	return NULL;
}

void XmlSerializer::MoveToChild(int index)
{
	currentNode = findChild(index);
}

void XmlSerializer::MoveToParent()
{
	currentNode = currentNode->parent;
}

bool XmlSerializer::CurrentValueAsBool()
{
	return atoi(CurrentValue()) != 0;
}

signed char XmlSerializer::CurrentValueAsSChar()
{
	return (signed char)CurrentValue()[0];
}

unsigned char XmlSerializer::CurrentValueAsUChar()
{
	return (unsigned char)CurrentValue()[0];
}

signed short int XmlSerializer::CurrentValueAsSShort()
{
	return (signed short int)atoi(CurrentValue());
}

unsigned short int XmlSerializer::CurrentValueAsUShort()
{
	return (unsigned short int)atoi(CurrentValue());
}

signed long int XmlSerializer::CurrentValueAsSLong()
{
	return (signed long int)atol(CurrentValue());
}

unsigned long int XmlSerializer::CurrentValueAsULong()
{
	return (unsigned long int)atol(CurrentValue());
}

float XmlSerializer::CurrentValueAsFloat()
{
	return (float)atof(CurrentValue());
}

double XmlSerializer::CurrentValueAsDouble()
{
	return atof(CurrentValue());
}

long double XmlSerializer::CurrentValueAsLongDouble()
{
	_LDOUBLE value;
	_atoldbl(&value, CurrentValue());
	return *((double*)value.ld);
}

signed long long XmlSerializer::CurrentValueAsSLongLong()
{
	return (signed long long)_atoi64(CurrentValue());	
}

unsigned long long XmlSerializer::CurrentValueAsULongLong()
{
	return (unsigned long long)_atoi64(CurrentValue());	
}

bool XmlSerializer::ChildValueAsBool(int index)
{
	return atoi(ChildValue(index)) != 0;
}

signed char XmlSerializer::ChildValueAsSChar(int index)
{
	return (signed char)ChildValue(index)[0];
}

unsigned char XmlSerializer::ChildValueAsUChar(int index)
{
	return (unsigned char)ChildValue(index)[0];
}

signed short int XmlSerializer::ChildValueAsSShort(int index)
{
	return (signed short int)atoi(ChildValue(index));
}

unsigned short int XmlSerializer::ChildValueAsUShort(int index)
{
	return (unsigned short int)atoi(ChildValue(index));
}

signed long int XmlSerializer::ChildValueAsSLong(int index)
{
	return (signed long int)atol(ChildValue(index));
}

unsigned long int XmlSerializer::ChildValueAsULong(int index)
{
	return (unsigned long int)atol(ChildValue(index));
}

float XmlSerializer::ChildValueAsFloat(int index)
{
	return (float)atof(ChildValue(index));
}

double XmlSerializer::ChildValueAsDouble(int index)
{
	return atof(ChildValue(index));
}

long double XmlSerializer::ChildValueAsLongDouble(int index)
{
	_LDOUBLE value;
	_atoldbl(&value, ChildValue(index));
	return *((double*)value.ld);
}

signed long long XmlSerializer::ChildValueAsSLongLong(int index)
{
	return (signed long long)_atoi64(ChildValue(index));	
}

unsigned long long XmlSerializer::ChildValueAsULongLong(int index)
{
	return (unsigned long long)_atoi64(ChildValue(index));	
}

xmlNodePtr XmlSerializer::findChild(int index)
{
	int elementsFound = -1;

	for (xmlNodePtr childNode = currentNode->children; childNode != currentNode->last->next; childNode = childNode->next)
	{
		if (childNode->type == XML_ELEMENT_NODE) 
		{
			elementsFound++;
			if (elementsFound == index)
			{
				return childNode;
			}
		}
	}

	return NULL;
}